﻿using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Ongoal.Quotation.Model;
using Polly;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Ongoal.Quotation.Http
{
    /// <summary>
    /// 
    /// </summary>
    public class MircoServiceClient : IMircoServiceClient
    {
        private readonly IHttpClientFactory _httpClientFactory;
        private readonly IConfiguration configuration;
        private readonly ServiceInfo _serviceInfo;
        private readonly DevSetting _devCfg;
        private readonly ILogger _logger;

        public MircoServiceClient(IHttpClientFactory httpClientFactory, IConfiguration cfg, ILogger<MircoServiceClient> logger)
        {
            _httpClientFactory = httpClientFactory;
            configuration = cfg;
            _serviceInfo = configuration.GetCfgValue<ServiceInfo>("BaseInfo");
            _devCfg = configuration.GetCfgValue<DevSetting>("DevSetting");
            _logger = logger;
        }

        /// <summary>
        /// 请求数据
        /// </summary>
        /// <param name="url">相对路径</param>
        /// <param name="httpMethod">请求方式</param>
        /// <param name="postData">请求数据</param>
        /// <param name="contentType">请求正文类型</param>
        /// <param name="timeOut">超时设置</param>
        /// <param name="headers">请求头设置</param>
        /// <returns></returns>
        public string SendRequest(string url, HttpMethod httpMethod, object postData, ContentType contentType = ContentType.Json, int timeOut = 30, Dictionary<string, string> headers = null)
        {
            return RequestService(url, httpMethod, postData, contentType, timeOut, headers).Result;
        }

        /// <summary>
        /// 异步请求Post数据
        /// </summary>
        /// <param name="url">相对路径</param>
        /// <param name="httpMethod">请求方式</param>
        /// <param name="postData">请求数据</param>
        /// <param name="contentType">请求正文类型</param>
        /// <param name="timeOut">超时设置</param>
        /// <param name="headers">请求头设置</param>
        /// <returns></returns>
        public async Task<string> SendRequestAsync(string url, HttpMethod httpMethod, object postData, ContentType contentType = ContentType.Json, int timeOut = 30, Dictionary<string, string> headers = null)
        {
            //请求重试
            ISyncPolicy policy = Policy.Handle<Exception>().WaitAndRetry(new[]
                {
                        TimeSpan.FromMilliseconds(1000),
                        TimeSpan.FromMilliseconds(1000)
                    }, (ex, timespan, count, Context) => {   });
            try
            {
                string request = "";
                request = await policy.Execute(async () =>
                {
                    string reur = url;
                    string rest = await RequestService(reur, httpMethod, postData, contentType, timeOut, headers);
                    return rest;
                });
                return request;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"请求内部服务出错,url:{url} 错误：" + ex.Message);
                _logger.LogError($"请求内部服务出错,url:{url} 错误：" + ex.Message);
                throw;
            }
        }


        private async Task<string> RequestService(string url, HttpMethod httpMethod, object postData, ContentType contentType = ContentType.Json, int timeOut = 30, Dictionary<string, string> headers = null)
        {
            var name = GetServiceName(url);
            var client = _httpClientFactory.CreateClient(name);
            if (!url.ToLower().StartsWith("http"))
            {
                if (client.BaseAddress == null)
                {
                    client.BaseAddress = new Uri(GetBaseAddress(name));
                }
                if (client.BaseAddress == null)
                {
                    throw new Exception($"获取服务{name}的失败");
                }
            }
            string postDataStr = postData != null ? JsonConvert.SerializeObject(postData) : "";
            using (var request = new HttpRequestMessage(httpMethod, url))
            {
                if (ServiceProviderContext.HttpContextAccessor != null && ServiceProviderContext.HttpContextAccessor.HttpContext != null)
                {
                    var curheads = ServiceProviderContext.HttpContextAccessor.HttpContext.Request.Headers;
                    string[] nohead = new string[] { "Accept", "Content-Type", "Transfer-Encoding", "Access-Control-Allow-Credentials", "Access-Control-Allow-Origin", "Connection", "Server", "Vary", "Content-Length" };
                    foreach (var citem in curheads)
                    {
                        if (nohead.Contains(citem.Key))
                        {
                            continue;
                        }

                        request.Headers.Add(citem.Key, citem.Value.ToString());
                    }
                    if (!request.Headers.Contains("internalrequest"))
                        request.Headers.Add("internalrequest", ServiceProviderContext.HttpContextAccessor.HttpContext.Request.Path);
                    if (!request.Headers.Contains("Authorization"))
                    {
                        //没有Token 或者是 调试模式
                        string token = GetLoginToken();
                        if (token == null)
                            throw new Exception("无法获取当前登录信息");
                        request.Headers.Add("Authorization", token);
                    }
                }
                if (headers != null)
                {
                    foreach (var item in headers)
                    {
                        request.Headers.Add(item.Key, item.Value);
                    }
                }
                using (HttpContent httpContent = new StringContent(postDataStr, Encoding.UTF8))
                {
                    request.Content = httpContent;
                    httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(contentType.ConvertToString());
                    HttpResponseMessage response = client.SendAsync(request).Result;
                    return await response.Content.ReadAsStringAsync();
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public string GetServiceName(string url)
        {
            var routerList = url.Split('/');
            if (routerList.Length > 0)
            {
                string serviceName = string.Empty;
                for (var i = 0; i < routerList.Length; i++)
                {
                    if (routerList[i].Length > 0 && !routerList[i].StartsWith("/"))
                    {
                        return routerList[i].ToLower();
                    }
                }
            }
            throw new Exception("无法解析的路由");
        }

        /// <summary>
        /// 获取微服务域名
        /// </summary>
        /// <param name="serviceName">服务名</param>
        /// <returns></returns>
        public string GetBaseAddress(string serviceName)
        {
            if (configuration["ASPNETCORE_ENVIRONMENT"] == "Development")
            {
                //调试模式
                var service = _devCfg.MicoServiceList.FirstOrDefault(item => item.Name.ToLower() == serviceName);
                if (service != null)
                {
                    return service.BaseAddress;
                }
            }
            else
            {
                //生产环境
                return _serviceInfo!.Gateway!;
            }
            return String.Empty;
        }


        private string GetLoginToken()
        {
            var request = ServiceProviderContext.HttpContextAccessor.HttpContext!.Request;
            if (request.Headers.Keys.Contains("Authorization"))
            {
                //有登录信息，直接返回Token
                return request.Headers["Authorization"];
            }
            else
            {
                var _jwt = ServiceProviderContext.ServiceProvider.GetService(typeof(IAuthentication)) as IAuthentication;
                var httpContext = ServiceProviderContext.HttpContextAccessor.HttpContext;
                if (_jwt != null)
                {
                    return _jwt.GetToken(httpContext!.Request.HttpContext.User.Claims.ToList());
                }
            }
            return null;

        }
    }
}
